package com.software.usercenter.judge;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.software.usercenter.entity.Question;
import com.software.usercenter.entity.Record;
import com.software.usercenter.utils.FileUtil;
import com.software.usercenter.utils.CommandUtil;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;


/**
 * Author: grx
 * Description: 编译、运行各类语言的程序
 */
@Component
public class Task {
    // 此处罗列出需要的临时文件(用于进程间通信)文件名约定
    // 所有的临时文件要放到这个目录中
    private String WORK_DIR;
    // 要编译执行的类的名字, 影响到源代码的文件名
    private String CLASS = "Solution";
    // 要编译执行的文件名
    private String CODE;
    // 程序标准输出放置的文件
    private String STDOUT;
    // 程序标准错误放置的文件
    private String STDERR;
    // 程序编译出错的详细信息放置的文件
    private String COMPILE_ERROR;

    public Task() {
        // 获取根目录
        String rootPath = "D:/WorkSpace/ShiXun2/cache";

        // 先生成唯一的id, 根据这个 id 来拼装出目录的名字
        WORK_DIR = rootPath + "/" + UUID.randomUUID().toString();
        // 然后再生成后续的这些文件名
        STDOUT = WORK_DIR + "/" + "stdout.txt";
        STDERR = WORK_DIR + "/" + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "/" + "compile_error.txt";
    }

    /**
     * 编译并运行.java文件
     * @param question 题目
     * @param record 用户的提交记录
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public Record compileAndRunJava(Question question, Record record) throws IOException, InterruptedException {
        // 获取根目录
        String rootPath = "/home/ubuntu/usr/workspace/cache";

        // 先生成唯一的id, 根据这个 id 来拼装出目录的名字
        WORK_DIR = rootPath + "/" + UUID.randomUUID().toString();
        // 然后再生成后续的这些文件名
        STDOUT = WORK_DIR + "/" + "stdout.txt";
        STDERR = WORK_DIR + "/" + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "/" + "compile_error.txt";

        // 1. 给这些临时文件准备好一个目录
        //    判定 WORD_DIR 是否存在. 如果存在就跳过. 不存在就创建目录
        File file = new File(WORK_DIR);
        if (!file.exists()) {
            // 创建对应的目录
            file.mkdirs();
        }
        CODE = WORK_DIR + "/" + CLASS + ".java";
        // 2. 先要准备好需要用到的临时文件
        //    要编译的源代码的文件(首先搞这个)
        //    编译出错要放进一个文件
        //    最终运行的标准输出标准错误也要分别放到文件中
        FileUtil.writeFile(CODE, record.getAnswer());

        // 3. 构造编译指令(javac), 并进行执行. 预期得到的结果
        //    就是一个对应的 .class 文件, 以及编译出错的文件
        //    -d 表示 生成的.class文件放置的位置
        //    javac -encoding utf-8 ./tmp/Solution.java -d ./tmp/
        // String compileCmd = "javac -encoding utf-8 " + CODE + " -d " + WORK_DIR;
        // String.format 类似于 C 中的 sprintf
        String compileCmd = String.format("javac %s", CODE);
        System.out.println("编译命令: " + compileCmd);
        // 创建子进程进行编译, 此处不关心 javac 的标准输出(没输出啥内容)
        // 只关心 javac 的标准错误. 标准错误中就包含了编译出错的信息
        CommandUtil.run("java", null, WORK_DIR, compileCmd, null, COMPILE_ERROR);
        // 此处判定一下编译是否出错. 看一下 COMPILE_ERROR 这个文件是不是空着就知道了
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("")) {
            // 编译错误的文件不为空, 说明编译出错了
            record.setStatus("Compile Error"); // 编译错误
            record.setReason(compileError);
            record.setNum(0);
        } else {
            // 获取起始时间
            long startTime = System.currentTimeMillis();

            // 4. 构造运行指令(java), 并进行执行. 预期得到的结果
            //    就是这个代码的标准输出的文件和标准错误的文件
            //    为了让 java 命令找到 .class 文件的位置, 还需要加上一个选项
            //    -classpath 通过这个选项来执行 .class 文件放到哪个目录里了
            //    java Solution
            String inputJson = question.getInputList();
            JSONObject inputList = JSONUtil.parseObj(inputJson);
            String outputJson = question.getOutputList();
            JSONObject outputList = JSONUtil.parseObj(outputJson);

            for (int i=1; i<=10; i++) {
                String inputContent = (String) inputList.get("input" + i);
                String inputFile = WORK_DIR + "/input" + i + ".txt";
                FileUtil.writeFile(inputFile, inputContent);
                ProcessBuilder pb = new ProcessBuilder("java", "-cp", WORK_DIR, "Solution");
                pb.redirectInput(new File(inputFile));
                pb.redirectOutput(new File(STDOUT));
                pb.redirectError(new File(STDERR));
                Process limitProcess = pb.start();

                Timer timer = new Timer();
                // 启动计时器
                timer.schedule(new KillTask(limitProcess, STDERR), 10 * 1000);
                // 关闭计时器
//                timer.cancel();

                int limitExitCode = limitProcess.waitFor();
                System.out.println("Limit process exited with code " + limitExitCode);
                if (limitExitCode == 139 || limitExitCode == 134) {
                    record.setStatus("Memory Limit Exceeded");
                    record.setNum(i-1);
                    return record;
                }
                // 尝试读取 STDERR 这个文件里的内容, 如果不为空, 就认为是运行出错
                // 如果程序抛出异常, 异常的调用栈信息就是通过 stderr 来输出的
                String runError = FileUtil.readFile(STDERR);
                if (!runError.equals("")) {
                    // 运行出错
                    record.setStatus("Running Error"); // 运行错误
                    record.setReason(runError);
                    record.setNum(i-1);
                    return record;
                } else if (runError == "Time Limit Exceeded") {
                    record.setStatus("Time Limit Exceeded");
                    record.setNum(i-1);
                } else {
                    // 对比输出结果是否正确
                    String outputContent = (String) outputList.get("output" + i);
//                String outputContent = FileUtil.readFile("D:\\WorkSpace\\ShiXun2\\cache\\123\\output1.txt");
                    String stdoutFile = FileUtil.readFile(STDOUT);
                    if (outputContent.equals(stdoutFile)) {
                        record.setStatus("Accepted"); // 答案正确
                        record.setNum(i);
                    } else {
                        record.setStatus("Wrong Answer"); // 答案错误
                        record.setNum(i-1);
                        return record;
                    }
                }
            }

            // 获取结束时间
            long endTime = System.currentTimeMillis();
            // 计算运行时长 s
            double time = (endTime - startTime) / 1000.0;
            record.setRunTime(time);
            // 判断是否运行超时
            if (time > question.getTimeLimit() * 10) {
                record.setStatus("Time Limit Exceeded"); // 运行超时
                return record;
            }

        }
        // 删掉临时文件夹
//        FileUtil.deleteFile(file);
        return record;
    }

    /**
     * 编译并运行C++文件
     * @param question
     * @param record
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public Record compileAndRunCPlusPlus(Question question, Record record) throws IOException, InterruptedException {
        /*// 获取根目录
        String rootPath = "D:/WorkSpace/ShiXun2/cache";

        // 先生成唯一的id, 根据这个 id 来拼装出目录的名字
        WORK_DIR = rootPath + "/" + UUID.randomUUID().toString();
        // 然后再生成后续的这些文件名
        STDOUT = WORK_DIR + "/" + "stdout.txt";
        STDERR = WORK_DIR + "/" + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "/" + "compile_error.txt";

        // 1. 给这些临时文件准备好一个目录
        //    判定 WORD_DIR 是否存在. 如果存在就跳过. 不存在就创建目录
        File file = new File(WORK_DIR);
        if (!file.exists()) {
            // 创建对应的目录
            file.mkdirs();
        }
        CODE = WORK_DIR + "/" + CLASS + ".cpp";
        // 2. 先要准备好需要用到的临时文件
        //    要编译的源代码的文件(首先搞这个)
        //    编译出错要放进一个文件
        //    最终运行的标准输出标准错误也要分别放到文件中
        FileUtil.writeFile(CODE, record.getAnswer());

        // 3. 构造编译指令(g++), 并进行执行. 预期得到的结果
        String compileCmd = String.format("g++ -o %s %s", CLASS, "Solution.cpp");
        System.out.println("编译命令: " + compileCmd);
        // 创建子进程进行编译
        CommandUtil.run("c/c++", "compile", WORK_DIR, compileCmd, null, COMPILE_ERROR);
        // 此处判定一下编译是否出错. 看一下 COMPILE_ERROR 这个文件是不是空着就知道了
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("")) {
            // 编译错误的文件不为空, 说明编译出错了
            record.setStatus("Compile Error"); // 编译错误
            record.setReason(compileError);
            return record;
        } else {
            // 获取起始时间
            long startTime = System.currentTimeMillis();
            // 获取起始空闲内存
            long startMemory = Runtime.getRuntime().freeMemory();

            // 4. 构造运行指令, 并进行执行. 预期得到的结果
            String inputJson = question.getInputList();
            JSONObject inputList = JSONUtil.parseObj(inputJson);
            String outputJson = question.getOutputList();
            JSONObject outputList = JSONUtil.parseObj(outputJson);
            for (int i=1; i<=1; i++) {
                String inputContent = (String) inputList.get("input" + i);
                String inputFile = WORK_DIR + "/input" + i + ".txt";
                FileUtil.writeFile(inputFile, inputContent);
                String runCmd = String.format("%s.exe < %s", "Solution", inputFile);
                CommandUtil.run("c/c++", inputFile, WORK_DIR, runCmd, STDOUT, STDERR);
//            CommandUtil.run("c++", "D:\\WorkSpace\\ShiXun2\\cache\\123\\input1.txt", WORK_DIR, "runCmd", STDOUT, STDERR);
                // 尝试读取 STDERR 这个文件里的内容, 如果不为空, 就认为是运行出错
                // 如果程序抛出异常, 异常的调用栈信息就是通过 stderr 来输出的
                String runError = FileUtil.readFile(STDERR);
                if (!runError.equals("")) {
                    // 运行出错
                    record.setStatus("Running Error"); // 运行错误
                    record.setReason(runError);
                } else if (runError == "Time Limit Exceeded") {
                    record.setStatus("Time Limit Exceeded");
                } else {
                    // 对比输出结果是否正确
                    String outputContent = (String) outputList.get("output" + i);
//                String outputContent = FileUtil.readFile("D:\\WorkSpace\\ShiXun2\\cache\\123\\output1.txt");
                    String stdoutFile = FileUtil.readFile(STDOUT);
//                System.out.println("标准答案: " + outputContent);
//                System.out.println("用户答案: " + stdoutFile);
                    if (outputContent.equals(stdoutFile)) {
                        record.setStatus("Accepted"); // 答案正确
                    } else {
                        record.setStatus("Wrong Answer"); // 答案错误
                        return record;
                    }
                }
            }

            // 获取结束时间
            long endTime = System.currentTimeMillis();
            // 计算运行时长 s
            double time = (endTime - startTime) / 1000.0;
            record.setRunTime(time);
            // 判断是否运行超时
            if (time > question.getTimeLimit() * 10) {
                record.setStatus("Time Limit Exceeded"); // 运行超时
                return record;
            }

            // 获取结束空闲内存
            long endMemory = Runtime.getRuntime().freeMemory();
            // 计算所占内存 MB
            double memory = (startMemory - endMemory) / (1024.0 * 1024.0);
            record.setRunMemory(memory);
            // 判断是否内存超限
            if (memory > question.getMemoryLimit() * 10) {
                record.setStatus("Memory Limit Exceeded"); // 内存超限
                return record;
            }
        }
        // 删掉临时文件夹
//        FileUtil.deleteFile(file);
        return record;*/


        // 获取根目录
        String rootPath = "/home/ubuntu/usr/workspace/cache";

        // 先生成唯一的id, 根据这个 id 来拼装出目录的名字
        WORK_DIR = rootPath + "/" + UUID.randomUUID().toString();
        // 然后再生成后续的这些文件名
        STDOUT = WORK_DIR + "/" + "stdout.txt";
        STDERR = WORK_DIR + "/" + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "/" + "compile_error.txt";

        // 1. 给这些临时文件准备好一个目录
        //    判定 WORD_DIR 是否存在. 如果存在就跳过. 不存在就创建目录
        File file = new File(WORK_DIR);
        if (!file.exists()) {
            // 创建对应的目录
            file.mkdirs();
        }
        CODE = WORK_DIR + "/" + CLASS + ".cpp";
        // 2. 先要准备好需要用到的临时文件
        //    要编译的源代码的文件(首先搞这个)
        //    编译出错要放进一个文件
        //    最终运行的标准输出标准错误也要分别放到文件中
        FileUtil.writeFile(CODE, record.getAnswer());

        // 3. 构造编译指令(g++), 并进行执行. 预期得到的结果
        String compileCmd = String.format("g++ -o %s %s", CLASS, "Solution.cpp");
        System.out.println("编译命令: " + compileCmd);
        // 创建子进程进行编译
        CommandUtil.run("c++", "compile", WORK_DIR, compileCmd, null, COMPILE_ERROR);
        // 此处判定一下编译是否出错. 看一下 COMPILE_ERROR 这个文件是不是空着就知道了
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("") && !compileError.equals("Time Limit Exceeded")) {
            // 编译错误的文件不为空, 说明编译出错了
            record.setStatus("Compile Error"); // 编译错误
            record.setReason(compileError);
            record.setNum(0);
            return record;
        } else {
            // 获取起始时间
            long startTime = System.currentTimeMillis();

            // 4. 构造运行指令, 并进行执行. 预期得到的结果
            String inputJson = question.getInputList();
            JSONObject inputList = JSONUtil.parseObj(inputJson);
            String outputJson = question.getOutputList();
            JSONObject outputList = JSONUtil.parseObj(outputJson);
            for (int i=1; i<=10; i++) {
                String inputContent = (String) inputList.get("input" + i);
                String inputFile = WORK_DIR + "/input" + i + ".txt";
                FileUtil.writeFile(inputFile, inputContent);

                ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ulimit -v " + (question.getMemoryLimit() * 1024) + "; exec \"$@\"", "--", WORK_DIR + "/Solution");
                pb.directory(new File(WORK_DIR));
                pb.redirectInput(new File(inputFile));
                pb.redirectErrorStream(true);
//                pb.redirectOutput(new File(STDOUT));
                Process limitProcess = pb.start();

                Timer timer = new Timer();
                // 启动计时器
                timer.schedule(new KillTask(limitProcess, STDERR), 10 * 1000);
                // 关闭计时器
//                timer.cancel();

                int limitExitCode = limitProcess.waitFor();
                System.out.println("Limit process exited with code " + limitExitCode);
                if (limitExitCode == 139 || limitExitCode == 134) {
                    record.setStatus("Memory Limit Exceeded");
                    record.setNum(i-1);
                    return record;
                } else if (limitExitCode == 137) {
                    record.setNum(i-1);
                    record.setStatus("Time Limit Exceeded");
                    return record;
                }

                if (STDOUT != null) {
                    // getInputStream 得到的是标准输出，得到里边的内容，为了后边进行重定向。
                    InputStream stdoutFrom = limitProcess.getInputStream();
                    BufferedReader bf = new BufferedReader(new InputStreamReader(stdoutFrom));
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = bf.readLine()) != null) {
                        sb.append(line);
                    }
                    String stdout = sb.toString();
                    System.out.println("output : " + stdout);
                    FileOutputStream stdoutTo = new FileOutputStream(STDOUT);
                    stdoutTo.write(stdout.getBytes());
                    // 关闭流
                    stdoutFrom.close();
                    bf.close();
                    stdoutTo.flush();
                    stdoutTo.close();
                }

                if (STDERR != null) {
                    // getErrorStream 得到的是标准错误
                    InputStream stderrFrom = limitProcess.getErrorStream();
                    // 表示弄了一个箱子，用来存放标准错误中的内容。
                    FileOutputStream stderrTo = new FileOutputStream(STDERR);
                    int ch;
                    while ((ch = stderrFrom.read()) != -1) {
                        stderrTo.write(ch);
                    }
                    // 操作完文件，进行文件的关闭。
                    stderrFrom.close();
                    stderrTo.flush();
                    stderrTo.close();
                }

//            CommandUtil.run("c++", "D:\\WorkSpace\\ShiXun2\\cache\\123\\input1.txt", WORK_DIR, "runCmd", STDOUT, STDERR);
                // 尝试读取 STDERR 这个文件里的内容, 如果不为空, 就认为是运行出错
                // 如果程序抛出异常, 异常的调用栈信息就是通过 stderr 来输出的
                String runError = FileUtil.readFile(STDERR);
                if (runError.equals("Time Limit Exceeded")) {
                    record.setStatus("Time Limit Exceeded");
                    return record;
                } else if (!runError.equals("")) {
                    // 运行出错
                    record.setStatus("Running Error"); // 运行错误
                    record.setReason(runError);
                    record.setNum(i-1);
                    return record;
                } else {
                    // 对比输出结果是否正确
                    String outputContent = (String) outputList.get("output" + i);
//                String outputContent = FileUtil.readFile("D:\\WorkSpace\\ShiXun2\\cache\\123\\output1.txt");
                    String stdoutFile = FileUtil.readFile(STDOUT);

//                System.out.println("标准答案: " + outputContent);
//                System.out.println("用户答案: " + stdoutFile);
                    if (outputContent.equals(stdoutFile)) {
                        record.setStatus("Accepted"); // 答案正确
                        record.setNum(i);
                    } else {
                        record.setStatus("Wrong Answer"); // 答案错误
                        record.setNum(i-1);
                        return record;
                    }
                }
            }

            // 获取结束时间
            long endTime = System.currentTimeMillis();
            // 计算运行时长 s
            double time = (endTime - startTime) / 1000.0;
            record.setRunTime(time);
            // 判断是否运行超时
            if (time > question.getTimeLimit() * 10) {
                record.setStatus("Time Limit Exceeded"); // 运行超时
                return record;
            }

        }
        // 删掉临时文件夹
//        FileUtil.deleteFile(file);
        return record;
    }

    /**
     * 编译并运行C文件
     * @param question
     * @param record
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public Record compileAndRunC(Question question, Record record) throws IOException, InterruptedException {
        /*// 获取根目录
        String rootPath = "D:/WorkSpace/ShiXun2/cache";

        // 先生成唯一的id, 根据这个 id 来拼装出目录的名字
        WORK_DIR = rootPath + "/" + UUID.randomUUID().toString();
        // 然后再生成后续的这些文件名
        STDOUT = WORK_DIR + "/" + "stdout.txt";
        STDERR = WORK_DIR + "/" + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "/" + "compile_error.txt";

        // 1. 给这些临时文件准备好一个目录
        //    判定 WORD_DIR 是否存在. 如果存在就跳过. 不存在就创建目录
        File file = new File(WORK_DIR);
        if (!file.exists()) {
            // 创建对应的目录
            file.mkdirs();
        }
        CODE = WORK_DIR + "/" + CLASS + ".cpp";
        // 2. 先要准备好需要用到的临时文件
        //    要编译的源代码的文件(首先搞这个)
        //    编译出错要放进一个文件
        //    最终运行的标准输出标准错误也要分别放到文件中
        FileUtil.writeFile(CODE, record.getAnswer());

        // 3. 构造编译指令(g++), 并进行执行. 预期得到的结果
        String compileCmd = String.format("g++ -o %s %s", CLASS, "Solution.cpp");
        System.out.println("编译命令: " + compileCmd);
        // 创建子进程进行编译
        CommandUtil.run("c/c++", "compile", WORK_DIR, compileCmd, null, COMPILE_ERROR);
        // 此处判定一下编译是否出错. 看一下 COMPILE_ERROR 这个文件是不是空着就知道了
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("")) {
            // 编译错误的文件不为空, 说明编译出错了
            record.setStatus("Compile Error"); // 编译错误
            record.setReason(compileError);
            return record;
        } else {
            // 获取起始时间
            long startTime = System.currentTimeMillis();
            // 获取起始空闲内存
            long startMemory = Runtime.getRuntime().freeMemory();

            // 4. 构造运行指令, 并进行执行. 预期得到的结果
            String inputJson = question.getInputList();
            JSONObject inputList = JSONUtil.parseObj(inputJson);
            String outputJson = question.getOutputList();
            JSONObject outputList = JSONUtil.parseObj(outputJson);
            for (int i=1; i<=1; i++) {
                String inputContent = (String) inputList.get("input" + i);
                String inputFile = WORK_DIR + "/input" + i + ".txt";
                FileUtil.writeFile(inputFile, inputContent);
                String runCmd = String.format("%s.exe < %s", "Solution", inputFile);
                CommandUtil.run("c/c++", inputFile, WORK_DIR, runCmd, STDOUT, STDERR);
//            CommandUtil.run("c++", "D:\\WorkSpace\\ShiXun2\\cache\\123\\input1.txt", WORK_DIR, "runCmd", STDOUT, STDERR);
                // 尝试读取 STDERR 这个文件里的内容, 如果不为空, 就认为是运行出错
                // 如果程序抛出异常, 异常的调用栈信息就是通过 stderr 来输出的
                String runError = FileUtil.readFile(STDERR);
                if (!runError.equals("")) {
                    // 运行出错
                    record.setStatus("Running Error"); // 运行错误
                    record.setReason(runError);
                } else if (runError == "Time Limit Exceeded") {
                    record.setStatus("Time Limit Exceeded");
                } else {
                    // 对比输出结果是否正确
                    String outputContent = (String) outputList.get("output" + i);
//                String outputContent = FileUtil.readFile("D:\\WorkSpace\\ShiXun2\\cache\\123\\output1.txt");
                    String stdoutFile = FileUtil.readFile(STDOUT);
//                System.out.println("标准答案: " + outputContent);
//                System.out.println("用户答案: " + stdoutFile);
                    if (outputContent.equals(stdoutFile)) {
                        record.setStatus("Accepted"); // 答案正确
                    } else {
                        record.setStatus("Wrong Answer"); // 答案错误
                        return record;
                    }
                }
            }

            // 获取结束时间
            long endTime = System.currentTimeMillis();
            // 计算运行时长 s
            double time = (endTime - startTime) / 1000.0;
            record.setRunTime(time);
            // 判断是否运行超时
            if (time > question.getTimeLimit() * 10) {
                record.setStatus("Time Limit Exceeded"); // 运行超时
                return record;
            }

            // 获取结束空闲内存
            long endMemory = Runtime.getRuntime().freeMemory();
            // 计算所占内存 MB
            double memory = (startMemory - endMemory) / (1024.0 * 1024.0);
            record.setRunMemory(memory);
            // 判断是否内存超限
            if (memory > question.getMemoryLimit() * 10) {
                record.setStatus("Memory Limit Exceeded"); // 内存超限
                return record;
            }
        }
        // 删掉临时文件夹
//        FileUtil.deleteFile(file);
        return record;*/


        // 获取根目录
        String rootPath = "/home/ubuntu/usr/workspace/cache";

        // 先生成唯一的id, 根据这个 id 来拼装出目录的名字
        WORK_DIR = rootPath + "/" + UUID.randomUUID().toString();
        // 然后再生成后续的这些文件名
        STDOUT = WORK_DIR + "/" + "stdout.txt";
        STDERR = WORK_DIR + "/" + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "/" + "compile_error.txt";

        // 1. 给这些临时文件准备好一个目录
        //    判定 WORD_DIR 是否存在. 如果存在就跳过. 不存在就创建目录
        File file = new File(WORK_DIR);
        if (!file.exists()) {
            // 创建对应的目录
            file.mkdirs();
        }
        CODE = WORK_DIR + "/" + CLASS + ".c";
        // 2. 先要准备好需要用到的临时文件
        //    要编译的源代码的文件(首先搞这个)
        //    编译出错要放进一个文件
        //    最终运行的标准输出标准错误也要分别放到文件中
        FileUtil.writeFile(CODE, record.getAnswer());

        // 3. 构造编译指令(g++), 并进行执行. 预期得到的结果
        String compileCmd = String.format("g++ -o %s %s", CLASS, "Solution.c");
        System.out.println("编译命令: " + compileCmd);
        // 创建子进程进行编译
        CommandUtil.run("c", "compile", WORK_DIR, compileCmd, null, COMPILE_ERROR);
        // 此处判定一下编译是否出错. 看一下 COMPILE_ERROR 这个文件是不是空着就知道了
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("") && !compileError.equals("Time Limit Exceeded")) {
            // 编译错误的文件不为空, 说明编译出错了
            record.setStatus("Compile Error"); // 编译错误
            record.setReason(compileError);
            record.setNum(0);
            return record;
        } else {
            // 获取起始时间
            long startTime = System.currentTimeMillis();

            // 4. 构造运行指令, 并进行执行. 预期得到的结果
            String inputJson = question.getInputList();
            JSONObject inputList = JSONUtil.parseObj(inputJson);
            String outputJson = question.getOutputList();
            JSONObject outputList = JSONUtil.parseObj(outputJson);
            for (int i=1; i<=10; i++) {
                String inputContent = (String) inputList.get("input" + i);
                String inputFile = WORK_DIR + "/input" + i + ".txt";
                FileUtil.writeFile(inputFile, inputContent);

                ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ulimit -v " + (question.getMemoryLimit() * 1024) + "; exec \"$@\"", "--", WORK_DIR + "/Solution");
                pb.directory(new File(WORK_DIR));
                pb.redirectInput(new File(inputFile));
                pb.redirectErrorStream(true);
//                pb.redirectOutput(new File(STDOUT));
                Process limitProcess = pb.start();

                Timer timer = new Timer();
                // 启动计时器
                timer.schedule(new KillTask(limitProcess, STDERR), 10 * 1000);
                // 关闭计时器
//                timer.cancel();

                int limitExitCode = limitProcess.waitFor();
                System.out.println("Limit process exited with code " + limitExitCode);
                if (limitExitCode == 139 || limitExitCode == 134) {
                    record.setStatus("Memory Limit Exceeded");
                    record.setNum(i-1);
                    return record;
                } else if (limitExitCode == 137) {
                    record.setStatus("Time Limit Exceeded");
                    record.setNum(i-1);
                    return record;
                }

                if (STDOUT != null) {
                    // getInputStream 得到的是标准输出，得到里边的内容，为了后边进行重定向。
                    InputStream stdoutFrom = limitProcess.getInputStream();
                    BufferedReader bf = new BufferedReader(new InputStreamReader(stdoutFrom));
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = bf.readLine()) != null) {
                        sb.append(line);
                    }
                    String stdout = sb.toString();
                    System.out.println("output : " + stdout);
                    FileOutputStream stdoutTo = new FileOutputStream(STDOUT);
                    stdoutTo.write(stdout.getBytes());
                    // 关闭流
                    stdoutFrom.close();
                    bf.close();
                    stdoutTo.flush();
                    stdoutTo.close();
                }

                if (STDERR != null) {
                    // getErrorStream 得到的是标准错误
                    InputStream stderrFrom = limitProcess.getErrorStream();
                    // 表示弄了一个箱子，用来存放标准错误中的内容。
                    FileOutputStream stderrTo = new FileOutputStream(STDERR);
                    int ch;
                    while ((ch = stderrFrom.read()) != -1) {
                        stderrTo.write(ch);
                    }
                    // 操作完文件，进行文件的关闭。
                    stderrFrom.close();
                    stderrTo.flush();
                    stderrTo.close();
                }

//            CommandUtil.run("c++", "D:\\WorkSpace\\ShiXun2\\cache\\123\\input1.txt", WORK_DIR, "runCmd", STDOUT, STDERR);
                // 尝试读取 STDERR 这个文件里的内容, 如果不为空, 就认为是运行出错
                // 如果程序抛出异常, 异常的调用栈信息就是通过 stderr 来输出的
                String runError = FileUtil.readFile(STDERR);
                if (!runError.equals("")) {
                    // 运行出错
                    record.setStatus("Running Error"); // 运行错误
                    record.setReason(runError);
                    record.setNum(i-1);
                } else if (runError.equals("Time Limit Exceeded")) {
                    record.setStatus("Time Limit Exceeded");
                    record.setNum(i-1);
                } else {
                    // 对比输出结果是否正确
                    String outputContent = (String) outputList.get("output" + i);
//                String outputContent = FileUtil.readFile("D:\\WorkSpace\\ShiXun2\\cache\\123\\output1.txt");
                    String stdoutFile = FileUtil.readFile(STDOUT);

//                System.out.println("标准答案: " + outputContent);
//                System.out.println("用户答案: " + stdoutFile);
                    if (outputContent.equals(stdoutFile)) {
                        record.setStatus("Accepted"); // 答案正确
                        record.setNum(i);
                    } else {
                        record.setStatus("Wrong Answer"); // 答案错误
                        record.setNum(i-1);
                        return record;
                    }
                }
            }

            // 获取结束时间
            long endTime = System.currentTimeMillis();
            // 计算运行时长 s
            double time = (endTime - startTime) / 1000.0;
            record.setRunTime(time);
            // 判断是否运行超时
            if (time > question.getTimeLimit() * 10) {
                record.setStatus("Time Limit Exceeded"); // 运行超时
                return record;
            }

        }
        // 删掉临时文件夹
//        FileUtil.deleteFile(file);
        return record;
    }

    public Record compileAndRunPython(Question question, Record record)  throws IOException, InterruptedException {
        // 获取根目录
        String rootPath = "/home/ubuntu/usr/workspace/cache";

        // 先生成唯一的id, 根据这个 id 来拼装出目录的名字
        WORK_DIR = rootPath + "/" + UUID.randomUUID().toString();
        // 然后再生成后续的这些文件名
        STDOUT = WORK_DIR + "/" + "stdout.txt";
        STDERR = WORK_DIR + "/" + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "/" + "compile_error.txt";

        // 1. 给这些临时文件准备好一个目录
        //    判定 WORD_DIR 是否存在. 如果存在就跳过. 不存在就创建目录
        File file = new File(WORK_DIR);
        if (!file.exists()) {
            // 创建对应的目录
            file.mkdirs();
        }
        CODE = WORK_DIR + "/" + CLASS + ".py";
        // 2. 先要准备好需要用到的临时文件
        //    要编译的源代码的文件(首先搞这个)
        //    编译出错要放进一个文件
        //    最终运行的标准输出标准错误也要分别放到文件中
        FileUtil.writeFile(CODE, record.getAnswer());

        // 获取起始时间
        long startTime = System.currentTimeMillis();

        // 3. 构造运行指令, 并进行执行. 预期得到的结果
        String inputJson = question.getInputList();
        JSONObject inputList = JSONUtil.parseObj(inputJson);
        String outputJson = question.getOutputList();
        JSONObject outputList = JSONUtil.parseObj(outputJson);
        for (int i=1; i<=10; i++) {
            String inputContent = (String) inputList.get("input" + i);
            String inputFile = WORK_DIR + "/input" + i + ".txt";
//            String content = FileUtil.readFile("D:\\WorkSpace\\ShiXun2\\cache\\123\\input1.txt");
            FileUtil.writeFile(inputFile, inputContent);

            /*String runCmd = String.format("python %s.py < %s", CLASS, inputFile);
//            String runCmd = String.format("python %s.py < %s", CLASS, "input1.txt");
            System.out.println("运行命令: " + runCmd);
            CommandUtil.run("python", null, WORK_DIR, runCmd, STDOUT, STDERR);*/
            ProcessBuilder pb = new ProcessBuilder("python3", CODE);
            pb.directory(new File(WORK_DIR));
            pb.redirectInput(new File(inputFile));
            pb.redirectOutput(new File(STDOUT));
            pb.redirectError(new File(STDERR));
            Process limitProcess = pb.start();
            System.out.println("memory limit : " + question.getMemoryLimit() + "MB");

            Timer timer = new Timer();
            // 启动计时器
            timer.schedule(new KillTask(limitProcess, STDERR), 10 * 1000);
            // 关闭计时器
//                timer.cancel();

            int limitExitCode = limitProcess.waitFor();
            System.out.println("Limit process exited with code " + limitExitCode);
            if (limitExitCode != 0) {
                record.setStatus("Memory Limit Exceeded");
                record.setNum(i-1);
                return record;
            }

            // 尝试读取 STDERR 这个文件里的内容, 如果不为空, 就认为是运行出错
            // 如果程序抛出异常, 异常的调用栈信息就是通过 stderr 来输出的
            String runError = FileUtil.readFile(STDERR);
            if (!runError.equals("")) {
                // 运行出错
                record.setStatus("Running Error"); // 运行错误
                record.setReason(runError);
                return record;
            } else if (runError == "Time Limit Exceeded") {
                record.setStatus("Time Limit Exceeded");
            } else {
                // 对比输出结果是否正确
                String outputContent = (String) outputList.get("output" + i);
//                String outputContent = FileUtil.readFile("D:\\WorkSpace\\ShiXun2\\cache\\123\\output1.txt");
                String stdoutFile = FileUtil.readFile(STDOUT);
                if (outputContent.equals(stdoutFile)) {
                    record.setStatus("Accepted"); // 答案正确
                    record.setNum(i);
                } else {
                    record.setStatus("Wrong Answer"); // 答案错误
                    record.setNum(i-1);
                    return record;
                }
            }
        }

        // 获取结束时间
        long endTime = System.currentTimeMillis();
        // 计算运行时长 s
        double time = (endTime - startTime) / 1000.0;
        record.setRunTime(time);
        // 判断是否运行超时
        if (time > question.getTimeLimit() * 10) {
            record.setStatus("Time Limit Exceeded"); // 运行超时
            return record;
        }


        // 删掉临时文件夹
//        FileUtil.deleteFile(file);
        return record;
    }

    static class  KillTask extends  TimerTask {
        private final Process process;
        private final String stderrFile;

        public KillTask(Process process, String stderrFile) {
            this.process = process;
            this.stderrFile = stderrFile;
        }

        public void run() {
            if (process != null && process.isAlive()) {
                // 销毁进程
                process.destroyForcibly();
                // 向错误文件中写入内容
                try {
                    String content = "Time Limit Exceeded";
                    // 创建文件写入流
                    FileWriter fileWriter = new FileWriter(stderrFile);
                    // 创建缓冲区写入流
                    BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
                    // 向文件中写入内容
                    bufferedWriter.write(content);
                    // 关闭流
                    bufferedWriter.flush();
                    bufferedWriter.close();
                    fileWriter.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /*public static void main(String[] args) throws IOException, InterruptedException {
        Task task = new Task();
        Record record = new Record();
        *//*record.setAnswer("#include <iostream>\n" +
                "\n" +
                "using namespace std;\n" +
                "\n" +
                "int main()\n" +
                "{\n" +
                "\tint a, b;\n" +
                "\tscanf(\"%d%d\", &a, &b);\n" +
                "\tprintf(\"%d\\n\", a+b);\n" +
                "\t\n" +
                "\treturn 0;\n" +
                "} ");*//*
        *//*record.setAnswer("import java.util.Scanner;\n" +
                "public class Solution {\n" +
                "        public static void main(String[] args) {\n" +
                "            Scanner scan = new Scanner(System.in);\n" +
                "            int a, b;\n" +
                "            a = scan.nextInt();\n" +
                "            b = scan.nextInt();\n" +
                "            System.out.println(a+b);\n" +
                "        }\n" +
                "    }");*//*
//        record.setAnswer("print(sum(map(int, input().split())))\n");
        Question question = new Question();
        question.setTimeLimit(1L);
        question.setMemoryLimit(1024L);
        Record ans = task.compileAndRunPython(question, record);
        System.out.println(ans.getStatus());
    }*/

}
